package aole.mindfx;

import java.io.File;
import java.io.IOException;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.ToolBar;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.xml.sax.SAXException;

import aole.mindfx.model.AppStructure;
import aole.mindfx.model.MindFXAttribute;
import aole.mindfx.model.MindFXCall;
import aole.mindfx.model.MindFXClass;
import aole.mindfx.model.MindFXMethod;
import aole.mindfx.profile.CppProfiler;
import aole.mindfx.profile.Profiler;

public class MindFX extends Application implements FXNodeListener {
	private BorderPane			root;
	private Pane				structurePane;
	private AppStructure		structure;
	private Node				createClassPane;
	private EditClassPane		editClassPane;
	private EditAttributePane	editAttributePane;
	private EditMethodPane		editMethodPane;
	private Profiler			profiler;

	private boolean initialize() {
		structure = new AppStructure( this );
		try {
			structure.load( new File( "test.xml" ) );
		} catch( ParserConfigurationException | SAXException | IOException e ) {
			e.printStackTrace();
			return false;
		}

		return true;
	}

	@Override
	public void start( Stage stage ) throws Exception {
		if( !initialize() ) {
			System.out.println( "Error: Could not initialize!" );
		}
		// scene
		root = new BorderPane();
		Scene scene = new Scene( root, 800, 600 );
		scene.setFill( Color.OLDLACE );

		// stage
		stage.setTitle( "MindFX" );
		stage.setScene( scene );

		profiler = new CppProfiler( structure );
		editClassPane = new EditClassPane( this, profiler );
		editAttributePane = new EditAttributePane( profiler );
		editMethodPane = new EditMethodPane( this, profiler, structure );

		structurePane = createClassesPane();
		createClassPane = createCreateClassPane();

		root.setTop( createToolBar() );
		root.setCenter( structurePane );

		stage.show();
	}

	private Node createCreateClassPane() {
		GridPane p = new GridPane();
		p.setPadding( new Insets( 12, 12, 12, 12 ) );

		p.add( new Label( "Create new Class" ), 1, 1, 2, 1 );
		p.add( new Label( "Name" ), 1, 2 );
		final TextField t = new TextField();
		p.add( t, 2, 2 );
		Button b = new Button( "Create" );
		b.setOnAction( new EventHandler< ActionEvent >() {
			@Override
			public void handle( ActionEvent event ) {
				actionPerformed( createClass( t.getText() ) );
				t.setText( "" );
			}
		} );

		p.add( b, 1, 3, 2, 1 );

		return p;
	}

	private MindFXClass createClass( String name ) {
		MindFXClass c = new MindFXClass( name, this );
		structure.addFXClass( c );
		profiler.typeAdded( name );
		structurePane.getChildren().add( c.getFXNode() );
		return c;
	}

	// Method simulation-----start
	public static class MethodSimulation {
		private IntegerProperty	sequence;
		private StringProperty	method;
		private StringProperty	purpose;

		public MethodSimulation( int l, String m, String p ) {
			sequence = new SimpleIntegerProperty( l );
			method = new SimpleStringProperty( m );
			purpose = new SimpleStringProperty( p );
		}

		public IntegerProperty sequenceProperty() {
			return sequence;
		}

		public StringProperty methodProperty() {
			return method;
		}

		public StringProperty purposeProperty() {
			return purpose;
		}
	}

	int	sequence;

	private void methodSimulatoin( ObservableList< MethodSimulation > data,
			MindFXMethod m, String pre ) {
		data.add( new MethodSimulation( sequence++, pre + m.getFullCaption(), m
				.getPurpose() ) );
		ObservableList< MindFXCall > calls = m.getFXCalls();
		for( MindFXCall call: calls ) {
			methodSimulatoin( data, call.getMethod(), pre + "\t" );
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private TableView< MethodSimulation > getSimulationTable( MindFXMethod m ) {
		TableColumn lCol = new TableColumn<>( "Sequence" );
		lCol.setCellValueFactory( new PropertyValueFactory( "sequence" ) );
		TableColumn mCol = new TableColumn<>( "Method" );
		mCol.setCellValueFactory( new PropertyValueFactory( "method" ) );
		TableColumn pCol = new TableColumn<>( "Purpose" );
		pCol.setCellValueFactory( new PropertyValueFactory( "purpose" ) );

		ObservableList< MethodSimulation > data = FXCollections
				.observableArrayList();
		sequence = 1;
		methodSimulatoin( data, m, "" );

		TableView< MethodSimulation > tableView = new TableView<>();
		tableView.setItems( data );
		tableView.getColumns().addAll( lCol, mCol, pCol );

		return tableView;
	}

	@Override
	public void onSimulation( MindFXMethod m ) {
		root.setCenter( getSimulationTable( m ) );
	}

	// Method simulation-----end

	private Pane createClassesPane() {
		FlowPane grid = new FlowPane();
		grid.setHgap( 5 );
		grid.setVgap( 5 );
		grid.setPadding( new Insets( 12, 12, 12, 12 ) );
		for( final MindFXClass c: structure.getAllClasses() ) {
			grid.getChildren().add( c.getFXNode() );
		}
		return grid;
	}

	private ToolBar createToolBar() {
		// New class
		Button b1 = new Button( "New Class" );
		b1.setStyle( "-fx-base:yellow" );
		b1.setOnAction( new EventHandler< ActionEvent >() {
			@Override
			public void handle( ActionEvent event ) {
				root.setRight( createClassPane );
			}
		} );

		// Save structure
		Button b2 = new Button( "Save Structure" );
		b2.setStyle( "-fx-base:yellow" );
		b2.setOnAction( new EventHandler< ActionEvent >() {
			@Override
			public void handle( ActionEvent event ) {
				try {
					structure.save( new File( "test.xml" ) );
				} catch( ParserConfigurationException | TransformerException e ) {
					System.out
							.println( "Error saving structure: " + "test.xml" );
					e.printStackTrace();
				}
			}
		} );

		// show structure
		Button b3 = new Button( "Show Structure" );
		b3.setStyle( "-fx-base:yellow" );
		b3.setOnAction( new EventHandler< ActionEvent >() {
			@Override
			public void handle( ActionEvent event ) {
				root.setCenter( structurePane );
			}
		} );

		return new ToolBar( b1, b2, b3 );
	}

	/**
	 * @param args
	 */
	public static void main( String[] args ) {
		launch( args );
	}

	@Override
	public void actionPerformed( MindFXClass c ) {
		editClassPane.setClass( c );
		root.setRight( editClassPane );
	}

	@Override
	public void actionPerformed( MindFXAttribute a ) {
		editAttributePane.setAttribute( a );
		root.setRight( editAttributePane );
	}

	@Override
	public void actionPerformed( MindFXMethod m ) {
		editMethodPane.setMethod( m );
		root.setRight( editMethodPane );
	}

	@Override
	public void showCode( MindFXClass fxclass ) {
		root.setCenter( profiler.getFXClassCode( fxclass ) );
	}

	@Override
	public void onDelete( MindFXClass c ) {
		structure.deleteFXNode(c);
		profiler.typeRemoved( c.getName() );
		structurePane.getChildren().remove( c.getFXNode() );
		root.setRight( createClassPane );
	}

	@Override
	public void onDelete( MindFXMethod m ) {
		structure.deleteFXNode(m);
	}

	@Override
	public void onDelete( MindFXAttribute a ) {
		structure.deleteFXNode(a);
	}

	@Override
	public void onDelete( MindFXCall c ) {
		structure.deleteFXNode(c);
	}

	@Override
	public AppStructure getStructure() {
		return structure;
	}

}
